package com.ruoyi.system.service.impl;

import java.time.LocalTime;
import java.util.Date;
import java.util.List;

import com.ruoyi.common.config.EmailConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.CaptchaExpireException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.EmailUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.quartz.domain.SysJob;
import com.ruoyi.quartz.service.impl.SysJobServiceImpl;
import com.ruoyi.quartz.util.CronUtils;
import com.ruoyi.system.domain.FreeTime;
import com.ruoyi.system.domain.TimeSlices;
import com.ruoyi.system.domain.vo.BookVo;
import com.ruoyi.system.domain.vo.SlicesDetailVo;
import com.ruoyi.system.mapper.FreeTimeMapper;
import com.ruoyi.system.mapper.TimeSlicesMapper;
import com.ruoyi.system.service.ISysConfigService;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import com.ruoyi.system.mapper.BookMapper;
import com.ruoyi.system.domain.Book;
import com.ruoyi.system.service.IBookService;
import org.springframework.transaction.annotation.Transactional;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

/**
 * 预约Service业务层处理
 *
 * @author WuZhongfu
 * @date 2022-02-17
 */
@Service("bookServiceImpl")
public class BookServiceImpl implements IBookService {
    @Autowired
    private BookMapper bookMapper;

    @Autowired
    private FreeTimeMapper freeTimeMapper;

    @Autowired
    private TimeSlicesMapper timeSlicesMapper;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    JavaMailSenderImpl mailSender;

    @Autowired
    private SysJobServiceImpl jobService;

    @Autowired
    EmailConfig emailConfig;

    @Autowired
    private ISysConfigService configService;

    /**
     * 查询预约
     *
     * @param id 预约主键
     * @return 预约
     */
    @Override
    public Book selectBookById(Long id) {
        return bookMapper.selectBookById(id);
    }

    /**
     * 查询预约列表
     *
     * @param book 预约
     * @return 预约
     */
    @Override
    public List<Book> selectBookList(Book book) {
        return bookMapper.selectBookList(book);
    }

    @Override
    public List<Book> selectBookNoCancel(Book book) {
        return bookMapper.selectBookNoCancel(book);
    }

    /**
     * 新增预约
     *
     * @param bookVo 预约
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertBook(BookVo bookVo) throws Exception {
        if (!"9527".equals(bookVo.getCode())) {
            //获取登录用户，如果没有登录需要验证码验证
            try {
                LoginUser loginUser = SecurityUtils.getLoginUser();
            } catch (Exception e) {
                String verifyKey = Constants.CAPTCHA_CODE_KEY + bookVo.getUuid();
                String captcha = redisCache.getCacheObject(verifyKey);
                redisCache.deleteObject(verifyKey);
                if (captcha == null) {
                    throw new CaptchaExpireException();
                }
                if (bookVo.getCode()==null||!bookVo.getCode().equalsIgnoreCase(captcha)) {
                    throw new CaptchaException();
                }
            }
        }

        //判断某人是否已经预约了该时间片
        Book book = new Book();
        book.setChildName(bookVo.getChildName());
        book.setPhone(bookVo.getPhone());
        book.setBookDate(bookVo.getBookDate());
        book.setStartTime(bookVo.getStartTime());
        book.setEndTime(bookVo.getEndTime());
        book.setStatus(0);
        List<Book> books = bookMapper.selectBooksTimeInterSect(book);
        if (books.size() > 0) {
            throw new Exception("该时间片和已预约时间片存在冲突");
        }

        //判断该时间片是否已经预约满
        Integer freeTimeId = bookVo.getFreeTimeId();
        FreeTime freeTime = freeTimeMapper.selectFreeTimeById(freeTimeId);
        TimeSlices timeSlices = timeSlicesMapper.selectTimeSlicesById(bookVo.getTimeSliceId());
        if (timeSlices.getNumBooked() >= freeTime.getNumLimit()) {
            throw new Exception("该时间段已预约满了，请预约其它时间段");
        }

        //判断当天是否预约过这个被预约者
        Book book1 = new Book();
        book1.setChildName(bookVo.getChildName());
        book1.setPhone(bookVo.getPhone());
        book1.setBookDate(bookVo.getBookDate());
        book1.setUserId(bookVo.getUserId());
        List<Book> books1 = bookMapper.selectBookList(book1);
        if(books1.size()>=2) {
            throw new Exception("不能预约同一个人同一天超过两次");
        }

        //获取被预约者称谓，
        String personReserved = configService.selectConfigByKey("sys.personReserved");
        if (personReserved == null || personReserved.length() < 1) {
            personReserved = "被预约者";
        }

        //预约时间和被预约者称谓语句，用于邮件发送内容拼接
        String timeAndPerson = "预约时间：" + DateUtils.parseDateToStr("yyyy-MM-dd", bookVo.getBookDate()) + "【" + bookVo.getStartTime() + "至" + bookVo.getEndTime() + "】，" + personReserved + "姓名：" + bookVo.getUserNickname();

        String from = emailConfig.getUsername();
        String to = bookVo.getEmail();

        //预约，相关数据插入表
        timeSlices.setNumBooked(timeSlices.getNumBooked() + 1);
        timeSlicesMapper.updateTimeSlices(timeSlices);
        int res = bookMapper.insertBook(bookVo);

        //是否立即发送消息，是的话发送邮件
        if (freeTime.getSendImmediate() == 1) {
            String text = freeTime.getSendImmediateContent();
            if (text == null || text.trim().length() < 1) {
                text = emailConfig.getSendImmediateContent();
            }
            String end = text.substring(text.length() - 1);
            if (!(".".equals(end) || "。".equals(end) || ",".equals(end) || "，".equals(end))) {
                text += "。";
            }
            text += timeAndPerson;
            String subject = emailConfig.getSendImmediateSubject();
            try {
                EmailUtils.sendNoMultiEmail(mailSender, from, to, subject, text);
            } catch (Exception e) {
                e.printStackTrace();
                throw new Exception("发送邮件失败，请检查邮箱地址是否能接收邮件");
            }
        }

        //设置提前提醒
        if (freeTime.getPreRemind() == 1) {

            //提前提醒时间（小时）
            Integer preRemindTime = freeTime.getPreRemindTime();

            //获得预约开始时间的毫秒数
            Date bookDate = bookVo.getBookDate();
            String startTime = bookVo.getStartTime();
            int hours = Integer.parseInt(startTime.substring(0, 2));
            int minutes = Integer.parseInt(startTime.substring(3, 5));
            long bookDateTimeMillis = bookDate.getTime();
            long startTimeMillis = bookDateTimeMillis + hours * 60 * 60 * 1000 + minutes * 60 * 1000;

            //当前毫秒数
            long nowTimeMillis = System.currentTimeMillis();

            //如果当前时间在提前提醒时间前，设置提前提醒定时器
            if (freeTime.getPreRemind() == 1 && startTimeMillis - preRemindTime * 60 * 60 * 1000 > nowTimeMillis) {
                String preRemindSubject = emailConfig.getPreRemindSubject();
                String preRemindContent = freeTime.getPreRemindContent();
                if (preRemindContent == null || preRemindContent.trim().length() < 1) {
                    preRemindContent = emailConfig.getPreRemindContent();
                }
                String end = preRemindContent.substring(preRemindContent.length() - 1);
                if (!(".".equals(end) || "。".equals(end) || ",".equals(end) || "，".equals(end))) {
                    preRemindContent += "。";
                }
                preRemindContent += timeAndPerson;

                //换掉英文小括号，避免影响定时器
                if (preRemindContent.contains(")")) {
                    preRemindContent = preRemindContent.replace(")", "】");
                    preRemindContent = preRemindContent.replace("(", "【");
                }
                SysJob job = new SysJob();
                Long job_id = jobService.selectMaxId();
                if (job_id!=null) {
                    job_id += 1;
                }
                else {
                    job_id=1L;
                }
                job.setJobId(job_id);
                bookVo.setJobId(job_id);
                Integer totalMinutes = hours * 60 + minutes - preRemindTime;
                Integer preHour = totalMinutes / 60;
                Integer preMinute = totalMinutes % 60;
                String cronExpression = "0 " + preMinute + " " + preHour + " " + bookDate.getDate() + " " + (bookDate.getMonth() + 1) + " " + "? " + (bookDate.getYear() + 1900);
                job.setCronExpression(cronExpression);
                String invokeTarget = "bookServiceImpl.sendNoMultiEmail('" + from + "','" + to + "','" + preRemindSubject + "','" + preRemindContent + "'," + job_id + "L)";
                job.setInvokeTarget(invokeTarget);
                job.setJobName(bookVo.getChildName() + "预约" + bookVo.getUserNickname());
                job.setStatus("0");
                job.setJobGroup("DEFAULT");
                job.setConcurrent("0");
                job.setMisfirePolicy("3");
                if (!CronUtils.isValid(job.getCronExpression())) {
                    throw new Exception("新增任务'" + job.getJobName() + "'失败，Cron表达式不正确");
                } else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) {
                    throw new Exception("新增任务'" + job.getJobName() + "'失败，目标字符串不允许'rmi://'调用");
                } else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_LDAP)) {
                    throw new Exception("新增任务'" + job.getJobName() + "'失败，目标字符串不允许'ldap://'调用");
                } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{Constants.HTTP, Constants.HTTPS})) {
                    throw new Exception("新增任务'" + job.getJobName() + "'失败，目标字符串不允许'http(s)//'调用");
                } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) {
                    throw new Exception("新增任务'" + job.getJobName() + "'失败，目标字符串存在违规");
                }
                job.setCreateBy("noLoginUser");
                try {
                    jobService.insertJob(job);
                } catch (Exception e) {
                    throw new Exception("新增提前提醒定时任务错误");
                }
            }
        }

        return res;
    }

    /**
     * 检查时间片是否预约满
     *
     * @param slice
     * @return
     */
    @Override
    public Boolean checkFull(SlicesDetailVo slice) throws Exception {
        //判断该时间片是否已经预约满
        FreeTime freeTime = freeTimeMapper.selectFreeTimeById(slice.getFreeTimeId());
        if (freeTime == null) {
            throw new Exception("数据库存在不合理数据，时间片对应的free_time不存在");
        }
        TimeSlices timeSlices = timeSlicesMapper.selectTimeSlicesById(slice.getId());
        if (timeSlices == null) {
            throw new Exception("该时间片不存在");
        }
        if (timeSlices.getNumBooked() >= freeTime.getNumLimit()) {
            throw new Exception("该时间片已预约满");
        }
        return false;
    }

    public void sendNoMultiEmail(String from, String to, String subject, String text, Long job_id) throws MessagingException {
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, false);
        helper.setSubject(subject);
        helper.setText(text);
        helper.setTo(to);
        helper.setFrom(from);
        mailSender.send(mimeMessage);
        Long[] ids = new Long[1];
        ids[0] = job_id;
        try {
            jobService.deleteJobByIds(ids);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 修改预约(状态)
     *
     * @param book 预约
     * @return 结果
     */
    @Override
    @Transactional
    public int updateBook(Book book) throws Exception {
        Integer status = book.getStatus();

        //取消预约
        if (status == 3) {
            //获取相关信息
            String personReserved = configService.selectConfigByKey("sys.personReserved");

            //时间片预约量减1
            timeSlicesMapper.BookedNumRedusOne(book.getTimeSliceId());

            //如果提前提醒定时器不为空，删除提前提醒定时器
            Long job_id = book.getJobId();
            if (job_id != null) {
                Long[] ids = new Long[1];
                ids[0] = job_id;
                try {
                    jobService.deleteJobByIds(ids);
                } catch (SchedulerException e) {
                    e.printStackTrace();
                }

            }

            //发邮件提醒预约已取消
            String startTime = book.getStartTime();
            String endTime = book.getEndTime();
            String userNickname = book.getUserNickname();
            String childName = book.getChildName();
            Date bookDate = book.getBookDate();
            String from = emailConfig.getUsername();
            String to = book.getEmail();
            String subject = "预约已取消通知";
            String text = childName + "于" + DateUtils.parseDateToStr("yyyy-MM-dd", bookDate) + "(" + startTime + "至" + endTime + ")的预约已被取消," + personReserved + "姓名：" + userNickname;
            try {
                EmailUtils.sendNoMultiEmail(mailSender, from, to, subject, text);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //标记来了或者标记爽约
        else if (status == 1 || status == 2) {
            Date bookDate = book.getBookDate();
            long time = bookDate.getTime();
            String endTime = book.getStartTime();
            LocalTime localTime = LocalTime.parse(endTime);
            time += localTime.getHour() * 3600 * 1000 + localTime.getMinute() * 60 * 1000 + localTime.getSecond() * 1000;
            long now = System.currentTimeMillis();
            if (now < time) {
                throw new Exception("预约时间未到，不能标记爽约或完成");
            }
        }
        return bookMapper.updateBook(book);
    }

    /**
     * 批量删除预约
     *
     * @param ids 需要删除的预约主键
     * @return 结果
     */
    @Override
    public int deleteBookByIds(Long[] ids) {
        return bookMapper.deleteBookByIds(ids);
    }

    /**
     * 删除预约信息
     *
     * @param id 预约主键
     * @return 结果
     */
    @Override
    public int deleteBookById(Long id) {
        return bookMapper.deleteBookById(id);
    }


}
